/***************************************************************************************************
                                项目                :       GimKeyboard
                                处理器				:       ESP32-S3
                                开发环境			:       pio(Arduino)
                                文件				:       ble_hid_server.cpp
                                说明				:       ble设备服务文件（依赖第三方库）
                                作者				:       金欣嵘
====================================================================================================

====================================================================================================
                                               修改记录 
====================================================================================================
----------------------------------------------------------------------------------------------------
                                               2024.02.05
----------------------------------------------------------------------------------------------------
                            修改人                  :            金欣嵘
                            修改记录                :            创建文件 
----------------------------------------------------------------------------------------------------
***************************************************************************************************/
#include <Arduino.h>
#include "../inc/ble_hid_server.h"
#include "../../hc165_keyboard_matrix_drv/inc/hc165_keyboard_matrix_drv.h"
#include "../../led_ws2812b_drv/inc/led_ws2812b_drv.h"

TaskHandle_t BleHidServer::ble_hid_server_task_handler = nullptr;
BleKeyboard BleHidServer::bleKeyboard("Gim-Keyboard", "GimXR", 100);

/***************************************************************************************************
  * @函数名称 : init
  * @函数功能 : 初始化ble服务任务
***************************************************************************************************/
void BleHidServer::init(void)
{
    /*GPIO INIT     BEGIN*/
    pinMode(BLE_CONNECTED_LED_PIN, OUTPUT);

    digitalWrite(BLE_CONNECTED_LED_PIN, HIGH);
    /*GPIO INIT     END*/

    /*BEL键盘库     BEGIN*/
    bleKeyboard.begin();
    /*BEL键盘库     END*/

    xTaskCreate(start_ble_hid_server_task,
    "BleHidServerTask",
    BLE_HID_SERVER_STACK_SIZE,
    NULL,
    BLE_HID_SERVER_TASK_PRIO,
    &ble_hid_server_task_handler);
}

/***************************************************************************************************
  * @函数名称 : ble_led_on
  * @函数功能 : 点亮连接指示灯
***************************************************************************************************/
void BleHidServer::ble_led_on(void)
{
    digitalWrite(BLE_CONNECTED_LED_PIN, LOW);
}

/***************************************************************************************************
  * @函数名称 : ble_led_off
  * @函数功能 : 熄灭连接指示灯
***************************************************************************************************/
void BleHidServer::ble_led_off(void)
{
    digitalWrite(BLE_CONNECTED_LED_PIN, HIGH);
}

/***************************************************************************************************
  * @函数名称 : map_trigger_and_hid_type
  * @输入参数 : trigger_id：触发的按键ID
  * @输出参数 : 无
  * @函数返回 : 键码
  * @函数功能 : 根据触发的按键索引键码
***************************************************************************************************/
uint8_t BleHidServer::map_trigger_and_hid_type(uint8_t trigger_id)
{
    uint8_t ret_val = 0;

    switch(trigger_id)
    {
        case KEYBOARD_KEY_F7:               ret_val = KEY_F7;           break;
        case KEYBOARD_KEY_F6:               ret_val = KEY_F6;           break;
        case KEYBOARD_KEY_F5:               ret_val = KEY_F5;           break;
        case KEYBOARD_KEY_F4:               ret_val = KEY_F4;           break;
        case KEYBOARD_KEY_F3:               ret_val = KEY_F3;           break;
        case KEYBOARD_KEY_F2:               ret_val = KEY_F2;           break;
        case KEYBOARD_KEY_F1:               ret_val = KEY_F1;           break;
        case KEYBOARD_KEY_ESC:              ret_val = KEY_ESC;          break;
        case KEYBOARD_KEY_F8:               ret_val = KEY_F8;           break;
        case KEYBOARD_KEY_F9:               ret_val = KEY_F9;           break;
        case KEYBOARD_KEY_F10:              ret_val = KEY_F10;          break;
        case KEYBOARD_KEY_F11:              ret_val = KEY_F11;          break;
        case KEYBOARD_KEY_F12:              ret_val = KEY_F12;          break;
        case KEYBOARD_KEY_PRINT_SCREEN:     ret_val = KEY_PRTSC;        break;
        case KEYBOARD_KEY_SCROLL_LOCK:      ret_val = KEY_CAPS_LOCK;    break;
        case KEYBOARD_KEY_PAUSE_BREAK:      ret_val = KEY_CAPS_LOCK;    break;
        case KEYBOARD_KEY_7:                ret_val = '7';              break;
        case KEYBOARD_KEY_6:                ret_val = '6';              break;
        case KEYBOARD_KEY_5:                ret_val = '5';              break;
        case KEYBOARD_KEY_4:                ret_val = '4';              break;
        case KEYBOARD_KEY_3:                ret_val = '3';              break;
        case KEYBOARD_KEY_2:                ret_val = '2';              break;
        case KEYBOARD_KEY_1:                ret_val = '1';              break;
        case KEYBOARD_KEY_TILDE:            ret_val = '`';              break;
        case KEYBOARD_KEY_HOME:             ret_val = KEY_HOME;         break;
        case KEYBOARD_KEY_INSERT:           ret_val = KEY_INSERT;       break;
        case KEYBOARD_KEY_BACKSPACE:        ret_val = KEY_BACKSPACE;    break;
        case KEYBOARD_KEY_PLUS_SIGN:        ret_val = '=';              break;
        case KEYBOARD_KEY_MINUS_SIGN:       ret_val = '-';              break;
        case KEYBOARD_KEY_0:                ret_val = '0';              break;
        case KEYBOARD_KEY_9:                ret_val = '9';              break;
        case KEYBOARD_KEY_8:                ret_val = '8';              break;
        case KEYBOARD_KEY_Y:                ret_val = 'y';              break;
        case KEYBOARD_KEY_T:                ret_val = 't';              break;
        case KEYBOARD_KEY_R:                ret_val = 'r';              break;
        case KEYBOARD_KEY_E:                ret_val = 'e';              break;
        case KEYBOARD_KEY_W:                ret_val = 'w';              break;
        case KEYBOARD_KEY_Q:                ret_val = 'q';              break;
        case KEYBOARD_KEY_TAB:              ret_val = KEY_TAB;          break;
        case KEYBOARD_KEY_PAGE_UP:          ret_val = KEY_PAGE_UP;      break;
        case KEYBOARD_KEY_DELETE:           ret_val = KEY_DELETE;       break;
        case KEYBOARD_KEY_CAESURA_SIGN:     ret_val = '\\';             break;
        case KEYBOARD_KEY_BACK_QUOTE:       ret_val = '}';              break;
        case KEYBOARD_KEY_FRONT_QUOTE:      ret_val = '{';              break;
        case KEYBOARD_KEY_P:                ret_val = 'p';              break;
        case KEYBOARD_KEY_O:                ret_val = 'o';              break;
        case KEYBOARD_KEY_I:                ret_val = 'i';              break;
        case KEYBOARD_KEY_U:                ret_val = 'u';              break;
        case KEYBOARD_KEY_G:                ret_val = 'g';              break;
        case KEYBOARD_KEY_F:                ret_val = 'f';              break;
        case KEYBOARD_KEY_D:                ret_val = 'd';              break;
        case KEYBOARD_KEY_S:                ret_val = 's';              break;
        case KEYBOARD_KEY_A:                ret_val = 'a';              break;
        case KEYBOARD_KEY_CAPS_LOCK:        ret_val = KEY_CAPS_LOCK;    break;
        case KEYBOARD_KEY_PAGE_DOWN:        ret_val = KEY_PAGE_DOWN;    break;
        case KEYBOARD_KEY_END:              ret_val = KEY_END;          break;
        case KEYBOARD_KEY_LEFT_SHIFT:       ret_val = KEY_LEFT_SHIFT;   break;
        case KEYBOARD_KEY_ENTER:            ret_val = '\n';             break;
        case KEYBOARD_KEY_QUOTATION_MARK:   ret_val = 39;               break;
        case KEYBOARD_KEY_COLON:            ret_val = ';';              break;
        case KEYBOARD_KEY_L:                ret_val = 'l';              break;
        case KEYBOARD_KEY_K:                ret_val = 'k';              break;
        case KEYBOARD_KEY_J:                ret_val = 'j';              break;
        case KEYBOARD_KEY_H:                ret_val = 'h';              break;
        case KEYBOARD_KEY_COMMA:            ret_val = ',';              break;
        case KEYBOARD_KEY_M:                ret_val = 'm';              break;
        case KEYBOARD_KEY_N:                ret_val = 'n';              break;
        case KEYBOARD_KEY_B:                ret_val = 'b';              break;
        case KEYBOARD_KEY_V:                ret_val = 'v';              break;
        case KEYBOARD_KEY_C:                ret_val = 'c';              break;
        case KEYBOARD_KEY_X:                ret_val = 'x';              break;
        case KEYBOARD_KEY_Z:                ret_val = 'z';              break;
        case KEYBOARD_KEY_SPACE:            ret_val = ' ';              break;
        case KEYBOARD_KEY_LEFT_ALT:         ret_val = KEY_LEFT_ALT;     break;
        case KEYBOARD_KEY_LEFT_WIN:         ret_val = KEY_LEFT_GUI;     break;
        case KEYBOARD_KEY_LEFT_CTRL:        ret_val = KEY_LEFT_CTRL;    break;
        case KEYBOARD_KEY_UP_ARROW:         ret_val = KEY_UP_ARROW;     break;
        case KEYBOARD_KEY_RIGHT_SHIFT:      ret_val = KEY_RIGHT_SHIFT;  break;
        case KEYBOARD_KEY_QUESTION_MARK:    ret_val = '/';              break;
        case KEYBOARD_KEY_PERIOD:           ret_val = '.';              break;
        case KEYBOARD_KEY_INVALID:          ret_val = 0;                break;
        case KEYBOARD_KEY_RIGHT_ARROW:      ret_val = KEY_RIGHT_ARROW;  break;
        case KEYBOARD_KEY_DOWN_ARROW:       ret_val = KEY_DOWN_ARROW;   break;
        case KEYBOARD_KEY_LEFT_ARROW:       ret_val = KEY_LEFT_ARROW;   break;
        case KEYBOARD_KEY_RIGHT_CTRL:       ret_val = KEY_RIGHT_CTRL;   break;
        case KEYBOARD_KEY_RIGHT_CLICK:      ret_val = 0;                break;  //鼠标左键
        case KEYBOARD_KEY_FN:               ret_val = 0;                break;  //FN
        case KEYBOARD_KEY_RIGHT_ALT:        ret_val = KEY_RIGHT_ALT;    break;
        default:                                                        break;
    }

    return ret_val;
}

/***************************************************************************************************
  * @函数名称 : tirgger_fn_func
  * @函数功能 : 触发FN按键逻辑
***************************************************************************************************/
void BleHidServer::tirgger_fn_func(void)
{
    if (keyboard_matrix_drv.trigger_matrix[KEYBOARD_KEY_FN / HC165_DATA_PIN_NUM] >> (KEYBOARD_KEY_FN % HC165_DATA_PIN_NUM))
    {
        if (keyboard_matrix_drv.trigger_matrix[KEYBOARD_KEY_ESC / HC165_DATA_PIN_NUM] >> (KEYBOARD_KEY_ESC % HC165_DATA_PIN_NUM))
        {
            printf("[BLE SERVER]service end!");
            bleKeyboard.end();
        }

        if (keyboard_matrix_drv.trigger_matrix[KEYBOARD_KEY_UP_ARROW / HC165_DATA_PIN_NUM] >> (KEYBOARD_KEY_UP_ARROW % HC165_DATA_PIN_NUM))
        {
            printf("[LED COLOR]color pipe value change: up!\n");
            if (keyboard_matrix_drv.trigger_matrix[KEYBOARD_KEY_R / HC165_DATA_PIN_NUM] >> (KEYBOARD_KEY_R % HC165_DATA_PIN_NUM))
            {
                led_2812b_drv.set_color_as_step(WS2812B_COLOR_PIPE_R, 10, COLOR_CHANGE_DIRECTION_UP);
            }
            else if (keyboard_matrix_drv.trigger_matrix[KEYBOARD_KEY_G / HC165_DATA_PIN_NUM] >> (KEYBOARD_KEY_G % HC165_DATA_PIN_NUM))
            {
                led_2812b_drv.set_color_as_step(WS2812B_COLOR_PIPE_G, 10, COLOR_CHANGE_DIRECTION_UP);
            }
            else if (keyboard_matrix_drv.trigger_matrix[KEYBOARD_KEY_B / HC165_DATA_PIN_NUM] >> (KEYBOARD_KEY_B % HC165_DATA_PIN_NUM))
            {
                led_2812b_drv.set_color_as_step(WS2812B_COLOR_PIPE_B, 10, COLOR_CHANGE_DIRECTION_UP);
            }
        }

        else if (keyboard_matrix_drv.trigger_matrix[KEYBOARD_KEY_DOWN_ARROW / HC165_DATA_PIN_NUM] >> (KEYBOARD_KEY_DOWN_ARROW % HC165_DATA_PIN_NUM))
        {
            printf("[LED COLOR]color pipe value change: down!\n");
            if (keyboard_matrix_drv.trigger_matrix[KEYBOARD_KEY_R / HC165_DATA_PIN_NUM] >> (KEYBOARD_KEY_R % HC165_DATA_PIN_NUM))
            {
                led_2812b_drv.set_color_as_step(WS2812B_COLOR_PIPE_R, 10, COLOR_CHANGE_DIRECTION_DOWN);
            }
            else if (keyboard_matrix_drv.trigger_matrix[KEYBOARD_KEY_G / HC165_DATA_PIN_NUM] >> (KEYBOARD_KEY_G % HC165_DATA_PIN_NUM))
            {
                led_2812b_drv.set_color_as_step(WS2812B_COLOR_PIPE_G, 10, COLOR_CHANGE_DIRECTION_DOWN);
            }
            else if (keyboard_matrix_drv.trigger_matrix[KEYBOARD_KEY_B / HC165_DATA_PIN_NUM] >> (KEYBOARD_KEY_B % HC165_DATA_PIN_NUM))
            {
                led_2812b_drv.set_color_as_step(WS2812B_COLOR_PIPE_B, 10, COLOR_CHANGE_DIRECTION_DOWN);
            }
        }
    }
}

/***************************************************************************************************
  * @函数名称 : send_trigger_key
  * @输入参数 : trigger_matrix：按键触发矩阵
  * @输出参数 : 无
  * @函数返回 : 无
  * @函数功能 : ble触发按键
***************************************************************************************************/
void BleHidServer::send_trigger_key(uint8_t * trigger_matrix)
{
    if (trigger_matrix == nullptr)
    {
        return;
    }

    static bool s_key_triggered[HC165_NUM * HC165_DATA_PIN_NUM] = {false};      //用于保存按键已触发状态

    if (keyboard_matrix_drv.get_fn_key_state())
    {
        if (xTaskGetTickCount() % 100 == 0)
        {
            tirgger_fn_func();
        }
        
        return;
    }

    for (uint16_t i = 0; i < HC165_NUM * HC165_DATA_PIN_NUM; i ++)
    {
        if (keyboard_matrix_drv.trigger_matrix[i / HC165_DATA_PIN_NUM] >> (i % HC165_DATA_PIN_NUM) == KEYBOARD_PUSH && s_key_triggered[i] == false)
        {
            s_key_triggered[i] = true;
            bleKeyboard.press(map_trigger_and_hid_type(i));
        }
        else if (keyboard_matrix_drv.trigger_matrix[i / HC165_DATA_PIN_NUM] >> (i % HC165_DATA_PIN_NUM) == KEYBOARD_RELEASE && s_key_triggered[i] == true)
        {
            s_key_triggered[i] = false;
            bleKeyboard.release(map_trigger_and_hid_type(i));
        }
    }
}

/***************************************************************************************************
  * @函数名称 : start_ble_hid_server_task
  * @输入参数 : pvParameters：任务服务函数的参数（未使用）
  * @输出参数 : 无
  * @函数返回 : 无
  * @函数功能 : 任务服务函数
***************************************************************************************************/
void BleHidServer::start_ble_hid_server_task(void *pvParameters)
{

#if BLE_HID_SERVER_DEBUG
    printf("============ble server task is running!===========\r\n");
#endif

    for(;;)
    {
        if (bleKeyboard.isConnected())
        {
            ble_led_on();
            xSemaphoreGive(keyboard_matrix_drv.matrix_sacn_begin_semaphore);    //释放信号量扫描

            uint8_t trigger_matrix[HC165_NUM] = {0};
            xQueueReceive(keyboard_matrix_drv.keyboard_matrix_queue, trigger_matrix, portMAX_DELAY);    //等待有效的数据

#if BLE_HID_SERVER_DEBUG
            printf("[KEYBOARD MATRIX]data receive: ");
            for (uint16_t i = 0; i < HC165_NUM; i ++)
            {
                printf("%02x ", trigger_matrix[i]);
            }
            printf(" | queue len: %d!\n", uxQueueMessagesWaiting(keyboard_matrix_drv.keyboard_matrix_queue));
#endif

            send_trigger_key(trigger_matrix);
        }
        else
        {
            ble_led_off();
        }

        vTaskDelay(1);
    }

#if BLE_HID_SERVER_DEBUG
    printf("==============ble server task is end!=============\r\n");
#endif
}

BleHidServer ble_hid_server;
